package mail.fetch.m21cn;

import mail.entity.ProxyEntity;
import mail.exception.VerifyException;
import mail.util.*;

import javax.swing.*;
import java.io.IOException;
import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static com.han.http.util.Print.printlnInfo;
import static mail.util.StatusCode.MAIL21_LOGIN_SUCCESS;

/**
 * @author: Hanjiafeng
 * @date: Created in 10:32 2019/3/13
 * @desc: 21cn邮箱登录器
 */
class Login11 {

    /**
     * 定义发送对象Builder
     */
    private HttpClient.Builder clientBuilder;
    /**
     * 定义发送对象
     */
    private HttpClient httpClient;
    /**
     * 定义请求Builder
     */
    private HttpRequest.Builder requestBuilder;
    /**
     * 定义cookie管理器
     */
    private CookieManager cookieManager;
    /**
     * 用户名
     */
    private String account;
    /**
     * 密码
     */
    private String password;
    /**
     * js引擎调用器
     */
    private JsInvoker jsInvoker;
    private Map<String, String> parameterMap;

    Login11(ProxyEntity proxyEntity) {
        //初始化参数map
        parameterMap = new HashMap<>();

        //初始化CookieManager并设置接受所有cookie
        cookieManager = new CookieManager();
        cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);

        clientBuilder = HttpClient.newBuilder()
                .connectTimeout(Duration.ofMinutes(1))
                .cookieHandler(cookieManager)
                //禁止自动跳转
                .followRedirects(HttpClient.Redirect.NEVER);
        //如果代理不为null,设置代理
        if (!Objects.isNull(proxyEntity)) {
            clientBuilder.proxy(ProxySelector.of(new InetSocketAddress(proxyEntity.getIp(), proxyEntity.getPort())));
        }
        //设置通用参数
        httpClient = clientBuilder
                .build();
        //设置通用参数
        requestBuilder = HttpRequest.newBuilder()
                .timeout(Duration.ofMinutes(1))
                .setHeader("Accept-Encoding", "gzip, deflate, br")
                .setHeader("Accept-Language", "zh-CN,zh;q=0.9")
                .setHeader("DNT", "1")
                .setHeader("Upgrade-Insecure-Requests", "1")
                .setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3664.3 Safari/537.36");

        //初始化js引擎调用器
        jsInvoker = new JsInvoker("F:\\workspace\\java\\MailCrawler\\src\\main\\resources\\Encrypt21CN.js");
    }

    CookieManager getCookieManager(String account, String password) throws IOException, InterruptedException, VerifyException {

        this.account = account;
        this.password = password;

        reportRest();
        unifyLogin();
        analysis();
        captchaCheckout();
        commitProof();

        return cookieManager;
    }

    private void reportRest() throws IOException, InterruptedException {
        String[] headers = {
                "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
        };

        String uri = "https://mail.21cn.com/w2/";
        String body = PublicMethod.getResponseElement(httpClient, requestBuilder, uri, ResponseElement.BODY, headers);
        //访问id
        String accessId = RegexSearcher.matchFirst(body, "(?<=\\?accessId=)((?!&).)*(?=&)");
        parameterMap.put("accessId", accessId);
        String reportRestUri = "https://ux.21cn.com" + RegexSearcher.matchFirst(body, "(?<=')/api/htmlReportRest/getJs.js((?!').)*(?=')");

        headers[1] = "*/*";
        PublicMethod.getResponseElement(httpClient, requestBuilder, reportRestUri, ResponseElement.NOTHING, headers);
    }

    private void unifyLogin() throws IOException, InterruptedException {
        String[] headers = {
                "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
        };
        String uri = "https://mail.21cn.com/w2/logon/UnifyLogin.do?t=" + System.currentTimeMillis();
        String unifyAccountLoginUri = PublicMethod.getResponseElement(httpClient, requestBuilder, uri,
                ResponseElement.LOCATION, headers);
        parameterMap.put("unifyAccountLoginUri", unifyAccountLoginUri);
        String body = PublicMethod.getResponseElement(httpClient, requestBuilder, unifyAccountLoginUri,
                ResponseElement.BODY, headers);
        //验证码token
        String captchaToken = RegexSearcher.matchFirst(body, "(?<=picCaptcha.do\\?token=)((?!&).)*(?=&)");
        parameterMap.put("captchaToken", captchaToken);
        String paramId = RegexSearcher.matchFirst(body, "(?<= paramId = \")((?!\").)*(?=\")");
        parameterMap.put("paramId", paramId);
        String lt = RegexSearcher.matchFirst(body, "(?<= lt = \")((?!\").)*(?=\")");
        parameterMap.put("lt", lt);
        //获取密码加密参数
        String rsaKey = RegexSearcher.matchFirst(body, "(?<=j_rsaKey\" value=\")((?!\").)*(?=\")");
        parameterMap.put("rsaKey", rsaKey);
    }

    /**
     * 获取收件箱参数
     *
     * @throws IOException          io
     * @throws InterruptedException in
     */
    private void analysis() throws IOException, InterruptedException {
        String[] headers = {
                "Accept", "*/*"
        };
        String accessId = parameterMap.get("accessId");
        String onlineUri = "https://webchat.7moor.com/online?accessId=" + accessId +
                "&location=mail.21cn.com" +
                "&referrer=" +
                "&callbackF=getChatConfig" +
                "&action=getOnlineStates" +
                "&__=";
        String body = PublicMethod.getResponseElement(httpClient, requestBuilder, onlineUri, ResponseElement.BODY, headers);
        String account = RegexSearcher.matchFirst(body, "(?<=\"account\":\")((?!\").)*(?=\")");
        String serviceUri = "https://user-analysis.7moor.com/service?action=page.load" +
                "&data=%7B%22userId%22%3Anull%2C%22sessionId%22%3Anull%2C%22account%22%3A%22" + account + "%22%2C%22" +
                "accessId%22%3A%22" + accessId + "%22%2C%22" +
                "platform%22%3A%7B%22browserName%22%3A%22Chrome%22%2C" +
                "%22browserVersion%22%3A%2273.0.3664.3%22%2C%22osInfo%22%3A%22Windows%2010%2064-bit%22%2C%22" +
                "platformDescription%22%3A%22Chrome%2073.0.3664.3%20on%20Windows%2010%2064-bit%22%2C%22" +
                "seosource%22%3A%22%E7%AB%99%E5%86%85%22%2C%22" +
                "seokeywords%22%3A%22%22%7D%2C%22page%22%3A%7B%22title%22%3A%2221CN%E4%B8%AA%E4%BA%BA%E9%82%AE%E7%AE%B1%22%2C%22" +
                "prevUrl%22%3A%22%22%2C%22currentUrl%22%3A%22https%3A%2F%2Fmail.21cn.com%2Fw2%2F%22%7D%2C%22type%22%3A%22load%22%2C%22" +
                "isOpenChat%22%3Afalse%2C%22rootDomain%22%3A%2221cn.com%22%7D" +
                "&callback=ubaGetCallback";
        body = PublicMethod.getResponseElement(httpClient, requestBuilder, serviceUri, ResponseElement.BODY, headers);
        //将下面的id放入CookieManager
        CookieStore cookieStore = cookieManager.getCookieStore();
        String pageId = RegexSearcher.matchFirst(body, "(?<=\"pageId\":\")((?!\").)*(?=\")");
        HttpCookie pageIdCookie = new HttpCookie("pageId", pageId);
        cookieStore.add(URI.create("/"), pageIdCookie);
        String userId = RegexSearcher.matchFirst(body, "(?<=\"userId\":\")((?!\").)*(?=\")");
        HttpCookie userIdCookie = new HttpCookie("userId", userId);
        cookieStore.add(URI.create("/"), userIdCookie);
        String sessionId = RegexSearcher.matchFirst(body, "(?<=\"sessionId\":\")((?!\").)*(?=\")");
        HttpCookie sessionIdCookie = new HttpCookie("sessionId", sessionId);
        cookieStore.add(URI.create("/"), sessionIdCookie);
    }

    private void captchaCheckout() throws IOException, InterruptedException {
        String[] headers = {
                "Accept", "application/json, text/javascript, */*; q=0.01",
                "Content-Type", "application/x-www-form-urlencoded; charset=UTF-8",
                "Referer", parameterMap.get("unifyAccountLoginUri")
        };
        String needCaptchaUri = "https://open.e.189.cn/api/logbox/oauth2/needcaptcha.do";
        String postData = "accountType=02" +
                "&userName=" + account +
                "&appKey=8013411507";
        String body = PublicMethod.getResponseElement(httpClient, requestBuilder, needCaptchaUri, postData,
                ResponseElement.BODY, headers);
        printlnInfo("是否需要验证码:" + body);
        if (body.equals(StatusCode.MAIL21_NEED_NOT_CAPTCHA)) {
            return;
        }
        String token = parameterMap.get("captchaToken");
        String picCaptchaUri = "https://open.e.189.cn/api/logbox/oauth2/picCaptcha.do?token=" + token +
                "&rnd=" + System.currentTimeMillis();
        headers[1] = "image/webp,image/apng,image/*,*/*;q=0.8";
        HttpRequest request = requestBuilder
                .copy()
                .headers(headers)
                .uri(URI.create(picCaptchaUri))
                .build();
        Path path = Path.of("F:\\workspace\\java\\MailCrawler\\src\\main\\resources\\cache\\VerifyImage.png");
        HttpResponse<Path> response = httpClient.send(request, HttpResponse.BodyHandlers.ofFile(path));
        Path bodyPath = response.body();
        printlnInfo(bodyPath);
    }

    private void commitProof() throws IOException, InterruptedException, VerifyException {
        String[] headers = {
                "Accept", "application/json, text/javascript, */*; q=0.01",
                "Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"
        };
        byte[] imageByte = FileManager.read("F:\\workspace\\java\\MailCrawler\\src\\main\\resources\\cache\\VerifyImage.png");
        ImageIcon img = new ImageIcon(imageByte);
        JLabel label = new JLabel(img);
        String v = JOptionPane.showInputDialog(label);

        String loginSubmitUri = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do";
        String rsaKey = parameterMap.get("rsaKey");
        String encryptedPassword = encryptPassword(password, rsaKey);
        String postData = "appKey=8013411507" +
                "&accountType=02" +
                "&userName=" + account +
                "&password=%7BRSA%7D" + encryptedPassword +
                "&validateCode=" + v +
                "&captchaToken=" + parameterMap.get("captchaToken") +
                "&returnUrl=https%3A%2F%2Fmail.21cn.com%2Fw2%2Flogon%2FunifyPlatformLogonReturn.do%3FLSID%3D" + CookieParser.getSpecifiedCookie(cookieManager, "LSID") +
                "&mailSuffix=" +
                "&dynamicCheck=FALSE" +
                "&clientType=10010" +
                "&cb_SaveName=0" +
                "&isOauth2=false" +
                "&state=" +
                "&paramId=" + parameterMap.get("paramId") +
                "&lt=" + parameterMap.get("lt");
        String body = PublicMethod.getResponseElement(httpClient, requestBuilder, loginSubmitUri, postData,
                ResponseElement.BODY, headers);
        String result = RegexSearcher.matchFirst(body, "(?<=\"result\":)[\\d]*");
        //登录失败
        if (!result.equals(MAIL21_LOGIN_SUCCESS)) {
            String msg = RegexSearcher.matchFirst(body, "(?<=\"msg\":\")((?!\").)*(?=\")");
            throw new VerifyException("21CN邮箱登录失败," + msg + ":" + account + "," + password);
        }
        //访问跳转链接
        headers[1] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8";
        String toUri = RegexSearcher.matchFirst(body, "(?<=\"toUrl\":\")((?!\").)*(?=\")");
        PublicMethod.getResponseElement(httpClient, requestBuilder, toUri, ResponseElement.NOTHING, headers[0], headers[1]);
    }

    private String encryptPassword(String pwd, String rsaKey) {
        String method = "encode('" + pwd + "','" + rsaKey + "')";

        return jsInvoker.invokeJs(method);
    }
}
